package com.example.aigc_education.service.impl;

//import com.agentsflex.llm.MessageResponse;
import com.agentsflex.llm.spark.SparkLlm;
//import com.agentsflex.message.AiMessage;
//import com.agentsflex.message.HumanMessage;
//import com.agentsflex.message.SystemMessage;
//import com.agentsflex.prompt.HistoriesPrompt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.aigc_education.common.BusinessException;
import com.example.aigc_education.domain.*;
import com.example.aigc_education.domain.dto.*;
import com.example.aigc_education.domain.dto.query.PlanQuery;
import com.example.aigc_education.domain.dto.query.ShowPlanQuery;
import com.example.aigc_education.domain.request.UpdatePlanCurrentModuleRequest;
import com.example.aigc_education.domain.vo.ListPlanVO;
import com.example.aigc_education.domain.vo.PlanVO;
import com.example.aigc_education.domain.dto.TestInfoDTO;
import com.example.aigc_education.domain.request.UpdatePlanTimeRequest;
import com.example.aigc_education.enums.PlanPriorityType;
import com.example.aigc_education.mapper.CourseMapper;
import com.example.aigc_education.mapper.TodoMapper;
import com.example.aigc_education.mapper.UserMapper;
import com.example.aigc_education.service.PaperService;
import com.example.aigc_education.service.PlanService;
import com.example.aigc_education.mapper.PlanMapper;
import com.example.aigc_education.service.ThemeService;
import com.example.aigc_education.service.converter.PlanConverter;
import com.example.aigc_education.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author 23918
* @description 针对表【stu_plan(学习计划表)】的数据库操作Service实现
* @createDate 2024-05-29 22:25:26
*/
@Slf4j
@Service
public class PlanServiceImpl extends ServiceImpl<PlanMapper, Plan>
    implements PlanService{

    @Resource
    private PlanMapper planMapper;

    @Resource
    private SparkLlm sparkLlm;

    @Resource
    private UserMapper userMapper;

    @Resource
    private TodoMapper todoMapper;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private ThemeService themeService;


    @Resource
    private PaperService paperService;

    @Resource
    private PlanConverter planConverter;

    /**
     * 模糊查询学习计划
     * <p>
     * 调用MyBatis-Plus的list方法搜索相关的学习计划数据
     * 其中使用LambdaQueryWrapper做条件容器，匹配用户id、状态为1；学习计划名字模糊查询；创建时间倒序排序
     *
     * @param planName 学习计划的名字
     * @return 学习计划集合，其中集合按照时间倒序排序
     */
    @Override
    public List<PlanVO> queryPlan(String planName){
        try{
            Long userId = SecurityUtils.getUserId();

            // 添加查询条件
            LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Plan::getUserId, userId)
                    .eq(Plan::getStatus, 1)     // status = 1, 正常状态
                    .like(Plan::getPlanName, planName)  // 模糊查询
                    .orderByDesc(Plan::getCreateTime);     // 根据时间倒序排序

            List<PlanVO> planVOS = this.list(queryWrapper).stream().map((item)-> {
                PlanVO planVO = planConverter.planToPlanVO(item);
                List<Integer> studyTime = new ArrayList<>();

                // 将字符串study time 拆分成整型集合
                String studyTimeString = item.getStudyTime();
                String[] studyTimeList = studyTimeString.split(",");
                for(String studyTimeDuration : studyTimeList){
                    studyTime.add(Integer.parseInt(studyTimeDuration));
                }

                planVO.setStudyTimeIn7Days(studyTime);
                return planVO;
            }).collect(Collectors.toList());

            return planVOS;
        }catch (Exception e){
            throw new BusinessException("模糊搜索学习计划失败");
        }

    }



    @Override
    public List<ListPlanVO> queryPlanV2(String planName, Long userId) {
        try{

            // 添加查询条件
            LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Plan::getUserId, userId)
                    .eq(Plan::getStatus, 1)     // status = 1, 正常状态
                    .like(Plan::getPlanName, planName);// 模糊查询

            //排序
            queryWrapper.orderByAsc(Plan::getCompletionProgress)
                    .orderByAsc(Plan::getPriority)
                    .orderByDesc(Plan::getCreateTime);

            // 执行分页查询
            List<Plan> result = planMapper.selectList(queryWrapper);

            // 将 Plan 转换为 ListPlanVO
            List<ListPlanVO> listPlanVOs = planConverter.plansToListPlanVOs(result);

            //转换后将根据currentModule里的id用themeMapper查询出当前章节名并赋值给title
            setPlanModuleTitle(listPlanVOs);

            return listPlanVOs;
        }catch (Exception e){
            throw new BusinessException("模糊搜索学习计划失败");
        }

    }

    @Override
    public List<ListPlanVO> carousel(Long userId) {
        // 添加查询条件
        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getUserId, userId)
                .eq(Plan::getStatus, 1);     // status = 1, 正常状态

        //不返回getCompletionProgress==1的计划
        queryWrapper.ne(Plan::getCompletionProgress, 1);

        //排序
        queryWrapper.orderByDesc(Plan::getCompletionProgress)
                .orderByAsc(Plan::getPriority)
                .orderByDesc(Plan::getCreateTime);


        //只要最符合条件的前3个
        queryWrapper.last("limit 0,3");

        // 执行分页查询
        List<Plan> result = planMapper.selectList(queryWrapper);

        // 将 Plan 转换为 ListPlanVO
        List<ListPlanVO> listPlanVOs = planConverter.plansToListPlanVOs(result);

        //转换后将根据currentModule里的id用themeMapper查询出当前章节名并赋值给title
        setPlanModuleTitle(listPlanVOs);

        return listPlanVOs;
    }


    /**
     * 获取主题测验情况
     *
     * 通过计划id定位相关的主题，先调用LambdaQueryWrapper作为条件容器，条件：计划id、状态为1
     * 先调用themeService的count方法获取总问题数，再增加isComplete为1（已完成）条件，
     * 然后再次调用count方法来获取已完成的问题数，最后未完成的问题数就是用总数减去已完成的问题数
     *
     * @param planId    计划id
     * @return          TestInfoVO对象，里面包含总问题数、完成问题数、未完成问题数
     */
    @Override
    public TestInfoDTO getTestInfo(Long planId) {
        try{

            int allQuiz = 0;      // 总问题数
            int completedQuiz = 0;   // 已完成的问题数

            LambdaQueryWrapper<Theme> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Theme::getPlanId, planId)
                    .eq(Theme::getStatus, 1);

            List<Theme> themes = themeService.list(queryWrapper);
            for (Theme item : themes){
                LambdaQueryWrapper<Paper> lambdaQueryWrapper = new LambdaQueryWrapper<>();
                lambdaQueryWrapper.eq(Paper::getThemeId, item.getThemeId())
                        .eq(Paper::getStatus, 1)
                        .eq(Paper::getPaperType, 0);    // 0是测验，1是抽验卡
                allQuiz += Math.toIntExact(paperService.count(lambdaQueryWrapper)) ;

                lambdaQueryWrapper.eq(Paper::getIsComplete, 1);
                completedQuiz += Math.toIntExact(paperService.count(lambdaQueryWrapper));
            }

            int unFinishedQuiz = allQuiz - completedQuiz;     // 未完成问题数


            return new TestInfoDTO(allQuiz, completedQuiz, unFinishedQuiz);

        }catch (Exception e){
            throw new BusinessException("获取主题测验情况失败");
        }
    }

    /**
     * 删除学习计划。
     *
     * 本方法通过指定的学习计划ID，对学习计划进行软删除（将状态设为0），
     * 通过调用Mybatis-Plus的getById定位待删除的学习计划数据，updateById更新该条学习计划数据，即为删除学习计划
     * 如果删除过程中出现异常，本方法将捕获该异常并抛出一个自定义的业务异常，
     * 异常信息提示“删除学习计划失败”，以便上层调用者能够明确知道删除操作未能成功。
     *
     * @param planId 需要删除的学习计划的ID。
     * @throws BusinessException 如果删除学习计划失败，抛出此异常。
     */
    @Override
    @Transactional
    public void deletePlan(Long planId) {
        try {
            Plan plan = this.getById(planId);
            plan.setStatus(0);
            this.updateById(plan);

        } catch (Exception e) {
            throw new BusinessException("删除学习计划失败");
        }
    }


    /**
     *  查询用户所有的学习计划
     *
     *  本方法通过用户id定位用户的所有的学习计划，获得plan集合
     *  调用planConverter将每个plan转化为planVO对象，将字符串studyTime转化为整型集合
     *  如果删除过程中出现异常，本方法将捕获该异常并抛出一个自定义的业务异常，
     *  异常信息提示“搜索所有学习计划失败”，以便上层调用者能够明确知道删除操作未能成功。
     *
     * @param userId    用户id
     * @return          planVO集合
     */
    @Override
    public List<PlanVO> getAllPlan(Long userId) {
        try{
            LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Plan::getUserId, userId)
                    .eq(Plan::getStatus, 1)// 未删除
                    .orderByDesc(Plan::getCreateTime);      // 根据创建时间倒序排序

            List<PlanVO> plans = this.list(queryWrapper).stream().map((item)-> {
                PlanVO planVO = planConverter.planToPlanVO(item);
                List<Integer> studyTime = new ArrayList<>();

                // 将字符串study time 拆分成整型集合
                String studyTimeString = item.getStudyTime();
                String[] studyTimeList = studyTimeString.split(",");
                for(String studyTimeDuration : studyTimeList){
                    studyTime.add(Integer.parseInt(studyTimeDuration));
                }

                planVO.setStudyTimeIn7Days(studyTime);
                return planVO;
            }).collect(Collectors.toList());

            return plans;
        }catch (Exception e){
            throw new BusinessException("搜索所有学习计划失败");
        }
    }




    @Override
    public PageDTO<PlanVO> queryPlanPage(Long userId, PlanQuery planQuery) {
        LambdaQueryWrapper<Plan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Plan::getUserId, userId);


        Page<Plan> page = planQuery.toMpPageDefaultSortByCreateTimeDesc();
//        Page<Plan> planPage = planMapper.selectPage(page, queryWrapper);

        Page<Plan> planPage = this.page(page, queryWrapper);

        return PageDTO.of(planPage, PlanVO.class);
    }

//    @Transactional
//    @Override
//    public int generatePlan(Long userId, Long planId, Course course, String studyCapacity) {
//        Plan plan = planMapper.selectOne(new LambdaQueryWrapper<Plan>().eq(Plan::getUserId, userId).eq(Plan::getPlanId, planId));
//        User user = userMapper.selectOne(new LambdaUpdateWrapper<User>().eq(User::getUserId, userId));
//        // 将课程id存入plan
//
//        int update = planMapper.update(plan, new LambdaUpdateWrapper<Plan>().eq(Plan::getUserId, userId).eq(Plan::getPlanId, planId));
//        if (update < 1) {
//            throw new BusinessException("SQL更新失败");
//        }
//        List<CourseDTO> courses = JSON.parseArray(course.getChapter(), CourseDTO.class);
//        int count = 0;
//        String studentConfiguration =  "\n" +
//                "[Student Configuration]\n" +
//                "\uD83C\uDFAFDepth: "+ user.getLevel() +"\n" +
//                "\uD83C\uDFAFStudying-Habit: " + user.getStudyHabit() + "\n" +
//                "\uD83E\uDDE0Learning-Style: " + user.getStudyStyle() + "\n" +
//                "\uD83D\uDDE3\uFE0FStudying-Capacity: " + studyCapacity + "\n" +
//                "\uD83C\uDF1FGoal: " + "\n" +
//                "\uD83C\uDF10Language: Chinese (Default)\n" +
//                "\uD83C\uDF10Learning-Course(json format): " ;;
//        SystemMessage systemMessage = new SystemMessage(ProducePlanPrompts.PRODUCE_PLAN_SYSTEM_PROMPT);
//        HistoriesPrompt historiesPrompt = new HistoriesPrompt();
//        historiesPrompt.addMessage(systemMessage);
//
//        ChapterDTO chapter = null;
//        HumanMessage humanMessage = null;
//        for (int i = 0; i < courses.size(); i++){
//            log.info("生成计划，课程：{}", courses.get(i));
//            chapter = courses.get(i).getChapter();
//            // 未将知识点进行分开
//            humanMessage = new HumanMessage(studentConfiguration + chapter.toString());
//            log.info("学生配置：{}", studentConfiguration + chapter.toString());
//            historiesPrompt.addMessage(humanMessage);
//            MessageResponse<AiMessage> chat = sparkLlm.chat(historiesPrompt);
//            String result = chat.getMessage().getContent().replace("```json", "").replace("```", "");
//            log.info("生成计划，课程：{}，生成结果：{}", courses.get(i), result);
//            List<TodoDTO> todos = null;
//            try {
//                todos = JSON.parseArray(result, TodoDTO.class);
//            } catch (Exception e) {
//                throw new BusinessException("JSON解析失败");
//            }
//            todos.forEach(System.out::println);
//            int finalI = i;
//            todos.forEach(todoDTO -> {
//                Todo todo = new Todo();
//                todo.setPlanId(planId);
//                todo.setTodoTitle(todoDTO.getTodoTitle());
//                todo.setSubTodos(JSON.toJSONString(todoDTO.getSubTodos()));
//                todo.setSuggestTime(todoDTO.getSuggestTime());
//                todo.setChapter(todoDTO.getChapter());
//                if (finalI == 0) {
//                    todo.setIsCurrentChapter(1);
//                }
//                todo.setKnowledgepointer(todoDTO.getKnowledgePointer());
//                todo.setIsComplete(0);
//                todo.setType(todoDTO.getType());
//                int insert = todoMapper.insert(todo);
//                if (insert < 1) {
//                    throw new BusinessException("SQL插入失败");
//                }
////                todo.setPlanId(planId);
////                System.out.println(todo);
////                String jsonString = JSON.toJSONString(todoDTO.getSubTodos());
////                System.out.println("JSON:" + jsonString);
////                List<SubTodoDTO> parse = JSON.parseArray(jsonString, SubTodoDTO.class);
////                System.out.println(parse);
//
//            });
//
//
//        }
//        return count;
//    }

//    @Override
//    public String getTestCapacity(Long userId, Long planId, String testContent) {
//        Plan plan = planMapper.selectOne(new LambdaQueryWrapper<Plan>().eq(Plan::getUserId, userId).eq(Plan::getPlanId, planId));
//        if (plan == null) {
//            throw new BusinessException("计划不存在");
//        }
//        Course course = courseMapper.selectOne(new LambdaQueryWrapper<Course>());
//        if (course == null) {
//            throw new BusinessException("课程不存在");
//        }
//        String actualSituation = "课程知识点：" + course.getChapter() + "\n" +
//                "学生当前计划目 \n" +
//                "学生学习情况：" + testContent + "\n";
//        SystemMessage systemMessage = new SystemMessage(ProduceUserDetailsPrompts.STU_CAPACITY_SYSTEM_PROMPT);
//        HistoriesPrompt historiesPrompt = new HistoriesPrompt();
//        historiesPrompt.addMessage(systemMessage);
//        HumanMessage humanMessage = new HumanMessage(actualSituation);
//        historiesPrompt.addMessage(humanMessage);
//        MessageResponse<AiMessage> chat = sparkLlm.chat(historiesPrompt);
//        return chat.getMessage().getContent();
//    }


    /**
     * 添加学习计划。
     *
     * 本方法用于将一个新的学习计划添加到系统中。通过调用planMapper的insert方法，
     * 将计划对象plan插入到数据库中。如果插入操作失败，捕获到异常，
     * 则抛出一个业务异常，提示“添加学习计划失败”。
     *
     * @param plan 待添加的学习计划对象，包含计划的详细信息。
     * @throws BusinessException 如果添加计划过程中发生异常，则抛出此异常。
     */
    @Override
    @Transactional
    public void addPlan(Plan plan) {
        try {
            planMapper.insert(plan);
        } catch (Exception e) {
            throw new BusinessException("添加学习计划失败");
        }
    }


    /**
     * 根据计划ID更新学习计划信息。
     *
     * 本方法通过提供计划对象和计划ID，更新数据库中相应计划的信息。如果更新过程中发生异常，
     * 则抛出一个自定义的业务异常，提示更新学习计划失败。
     *
     * @param plan 待更新的计划对象，包含新的计划信息。
     * @param planId 需要更新的计划的ID，用于定位特定的计划记录。
     * @throws BusinessException 如果更新操作失败，抛出此异常。
     */
    @Override
    @Transactional
    public void updatePlan(Plan plan, Long planId) {
        try {
            // 使用Lambda表达式构建更新条件，指定更新的计划ID。
            LambdaUpdateWrapper<Plan> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(Plan::getPlanId, planId);

            // 调用计划Mapper的update方法，更新数据库中的计划信息。
            planMapper.update(plan, wrapper);
        } catch (Exception e) {
            // 捕获更新过程中的任何异常，抛出业务异常。
            throw new BusinessException("更新学习计划失败");
        }
    }


    /**
     * 获取所有计划的列表。
     *
     * 本方法通过调用计划Mapper的selectList方法，查询数据库中的所有计划对象，并返回查询结果作为列表形式。
     * 无需传入特定的查询条件，因为查询所有计划的逻辑是固定的。
     *
     * @return List<Plan> 返回一个计划对象的列表，包含所有计划。
     */
    @Override
    public List<Plan> getPlan() {
        // 调用计划Mapper的selectList方法查询所有计划
        List<Plan> plans = planMapper.selectList(null);
        // 返回查询结果
        return plans;
    }


    /**
     * 修改今日的计时时间
     * @return 更新结果
     */
    @Override
    public boolean updatePlanTime(UpdatePlanTimeRequest updatePlanTimeRequest) {
        try {
            Plan plan = planMapper.selectById(updatePlanTimeRequest.getPlanId());
            String studyTime = plan.getStudyTime();
            String[] parts = studyTime.split(",");
            // 获取最后一个元素并转换成整数
            int lastNumber = Integer.parseInt(parts[parts.length - 1]);

            // 加上指定的数字
            lastNumber += updatePlanTimeRequest.getTimeMinute();

            // 替换原来的最后一个元素
            parts[parts.length - 1] = String.valueOf(lastNumber);

            // 重新拼接数组成字符串
            String newStudyTime = String.join(",", parts);
            plan.setStudyTime(newStudyTime);
            Integer newTotalStudyTime = updatePlanTimeRequest.getTimeMinute() + plan.getTotalStudyTime();
            plan.setTotalStudyTime(newTotalStudyTime);
            planMapper.updateById(plan);
            return true;
        } catch (Exception e) {
            // 捕获更新过程中的任何异常，抛出业务异常。
            throw new BusinessException("更新学习计划学习时间异常");
        }
    }

    @Override
    public List<ListPlanVO> getAllPlanV2(Long userId, ShowPlanQuery showPlanQuery) {
        try {
            // 创建分页对象
            Page<Plan> planPage = new Page<>(showPlanQuery.getPage(), showPlanQuery.getPageSize());

            // 构建查询条件
            LambdaQueryWrapper<Plan> filterWrapper = new LambdaQueryWrapper<>();
            filterWrapper.eq(Plan::getUserId, userId)
                    .eq(Plan::getStatus, 1);

            ShowPlanQuery.PlanFilter filter = showPlanQuery.getFilter();

            if (filter.getTags() != null && filter.getTags().length > 0) {
                List<Integer> priorities = new ArrayList<>();
                for (String tag : filter.getTags()) {
                    priorities.add(PlanPriorityType.fromTitle(tag).getValue());
                }
                filterWrapper.in(Plan::getPriority, priorities);
            }

            if (!filter.getShowCompleted()) {
                // 如果showCompleted为false，则只查询未完成的计划
                filterWrapper.ne(Plan::getCompletionProgress, 1.00);
            }else {
                filterWrapper.orderByAsc(Plan::getCompletionProgress);
            }

            //排序
            filterWrapper.orderByAsc(Plan::getPriority)
                    .orderByDesc(Plan::getCreateTime);

            // 执行分页查询
            Page<Plan> resultPage = planMapper.selectPage(planPage, filterWrapper);

            // 将 Plan 转换为 ListPlanVO
            List<ListPlanVO> listPlanVOs = planConverter.plansToListPlanVOs(resultPage.getRecords());

            //转换后将根据currentModule里的id用themeMapper查询出当前章节名并赋值给title
            setPlanModuleTitle(listPlanVOs);

            return listPlanVOs;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("搜索所有学习计划失败");
        }
    }

    private void setPlanModuleTitle(List<ListPlanVO> listPlanVOs) {
        for (ListPlanVO listPlanVO : listPlanVOs) {
            Long currentModuleId = listPlanVO.getCurrentModule().getCurrentId();
            if (currentModuleId != null && currentModuleId > 0) {
                Theme theme = themeService.getById(currentModuleId);
                if (theme != null) {
                    listPlanVO.getCurrentModule().setSuperCurrentId(theme.getSuperiorThemeId());
                    listPlanVO.getCurrentModule().setCurrentTitle(theme.getThemeTitle());
                    listPlanVO.setCurrentModule(listPlanVO.getCurrentModule());
                }
            }else {
                listPlanVO.getCurrentModule().setCurrentTitle("你还没有开始该计划");
                listPlanVO.setCurrentModule(listPlanVO.getCurrentModule());
            }
        }
    }

    @Override
    public boolean updateCurrentModule(UpdatePlanCurrentModuleRequest updateCurrentModuleRq) {
        try {
            Plan plan = planMapper.selectById(updateCurrentModuleRq.getPlanId());
            plan.setCurrentModule(updateCurrentModuleRq.getCurrentModuleId());
            planMapper.updateById(plan);
            return true;
        } catch (Exception e) {
            // 捕获更新过程中的任何异常，抛出业务异常。
            throw new BusinessException("更新当前章节异常");
        }
    }


    /**
     * 计划学习时间展示
     * @param planId 计划id
     * @return showLearnTimeVO
     */
    @Override
    public Map<String,Integer> showLearnTime(Long planId) {
        Plan plan = planMapper.selectById(planId);
        // 学习时长字符串
        String studyTimeString = plan.getStudyTime();

        // 使用split方法按逗号分隔字符串
        String[] durationArray = studyTimeString.split(",");
        // 创建一个List来存储学习时长
        Map<String,Integer> showLearnTimeVO = new LinkedHashMap<>();
        // 获取当前日期
        LocalDate currentDate = LocalDate.now();
        int maxTime = Integer.valueOf(durationArray[0]);
        for (int i = 0; i < durationArray.length; i++) {
            LocalDate date = currentDate.minusDays(durationArray.length - i - 1);
            // 创建一个日期格式化器
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd");
            // 格式化当前日期为字符串
            String dateString = date.format(formatter);
            showLearnTimeVO.put(dateString, Integer.valueOf(durationArray[i]));
            maxTime = Math.max(Integer.valueOf(durationArray[i]),maxTime);
        }
        showLearnTimeVO.put("学习时间", plan.getTotalStudyTime());
        showLearnTimeVO.put("最长学习时间", maxTime);
        return showLearnTimeVO;
    }

}




